package com.code2roc.xhood.asm;

import com.code2roc.xhood.model.ObscureConfig;
import com.code2roc.xhood.model.ObsureEnum;
import com.code2roc.xhood.util.ObscureUtil;
import org.apache.commons.lang3.StringUtils;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Opcodes;

import java.util.*;

public class ClassDefineVisitor extends ClassVisitor {
    private String packageName;
    private String superName;
    private String className;
    private String classPackageName;
    private List<String> interfaceList;
    private LinkedHashMap<String, String> modifyMap;
    private HashMap<String, String> sourceMap;
    private List<String> obscurePackages;
    private List<String> obscureIgnorePackages;
    private List<String> obscureIgnoreClasss;
    private boolean modify = false;

    public ClassDefineVisitor(ClassVisitor cv, ObscureConfig obscureConfig) {
        super(Opcodes.ASM9, cv);
        interfaceList = new ArrayList<>();
        modifyMap = new LinkedHashMap<>();
        sourceMap = new HashMap<>();
        this.packageName = obscureConfig.getPackageName();
        this.obscurePackages = obscureConfig.getPackages();
        this.obscureIgnorePackages = obscureConfig.getIgnorePackages();
        this.obscureIgnoreClasss = obscureConfig.getIgnoreClasss();
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        this.className = name.replace('/', '.');
        List<String> classNameList = new ArrayList<>(Arrays.asList(className.split("\\.")));
        String classSimpleName = classNameList.get(classNameList.size() - 1);
        classNameList.remove(classNameList.size() - 1);
        this.classPackageName = StringUtils.join(classNameList, ".");
        for (String item : interfaces) {
            interfaceList.add(item.replace('/', '.'));
        }
        this.superName = superName;

        modify = check(name);
        if (modify) {
            String obfuscatedSimpleCLassName = ObscureUtil.getObscureNameV1(classSimpleName, ObsureEnum.Class);
            classNameList.add(obfuscatedSimpleCLassName);
            String obfuscatedName = StringUtils.join(classNameList, "/");
            modifyMap.put(name, obfuscatedName);
            sourceMap.put(classSimpleName, obfuscatedSimpleCLassName);
            super.visit(version, access, obfuscatedName, signature, superName, interfaces);
        } else {
            super.visit(version, access, name, signature, superName, interfaces);
        }
    }

    public LinkedHashMap<String, String> getModifyMap() {
        return modifyMap;
    }

    public boolean isModify() {
        return modify;
    }

    public HashMap<String, String> getSourceMap() {
        return sourceMap;
    }

    private boolean check(String name) {
        boolean modifyInvoke = true;
        boolean exist = false;
        for (String p : obscurePackages) {
            if (classPackageName.startsWith(p)) {
                exist = true;
                break;
            }
        }
        if (!exist) {
            modifyInvoke = false;
        }
        for (String p : obscureIgnorePackages) {
            if (p.startsWith("*")) {
                if (classPackageName.endsWith(p.replace("*/", ""))) {
                    modifyInvoke = false;
                    break;
                }
            } else {
                if (p.equals(classPackageName)) {
                    modifyInvoke = false;
                    break;
                }
            }
        }
        for (String p : obscureIgnoreClasss) {
            if (p.startsWith("*")) {
                if (className.endsWith(p.replace("*", ""))) {
                    modifyInvoke = false;
                    break;
                }
            } else {
                if (p.equals(className)) {
                    modifyInvoke = false;
                    break;
                }
            }
        }
        return modifyInvoke;
    }
}
